
#include "Repetier.h"
#include "ui.h"

#if (!SDSUPPORT) && USBDISKSUPPORT
#include "usbdisk.h"

#include "usbdisk_CH376INC.H"
/* 附加的USB操作状态定义 */
#define		ERR_USB_UNKNOWN		0xFA	/* 未知错误,不应该发生的情况,需检查硬件或者程序错误 */

char tempLongFilename[LONG_FILENAME_LENGTH + 1];

spiusbdisk_opn_parent_folders_t spiusbdisk_opn_parent_folders;
spiusbdisk_mount_t spiusbdisk_mount;
spiusbdisk_ls_recursive_t spiusbdisk_ls_recursive;
spiusbdisk_ls_reset_t spiusbdisk_ls_reset;
spiusbdisk_ls_next_t spiusbdisk_ls_next;

spiusbdisk_open_t spiusbdisk_open;
spiusbdisk_close_t spiusbdisk_close;
spiusbdisk_seekSet_t spiusbdisk_seekSet;
spiusbdisk_remove_t spiusbdisk_remove;
spiusbdisk_mkdir_t spiusbdisk_mkdir;

spiusbdisk_fileSize_t spiusbdisk_fileSize;
spiusbdisk_chdir_t spiusbdisk_chdir;
spiusbdisk_fileRead_t spiusbdisk_fileRead;
spiusbdisk_fileWrite_t spiusbdisk_fileWrite;
spiusbdisk_getLongName_t spiusbdisk_getLongName;

USBDisk usbdisk;

USBDisk::USBDisk()
{
    sdmode = 0;
    sdactive = false;
    savetosd = false;

#if defined (__SAM3X8E__)
    sdmode_ff = 0;
    sdmode_ff_pos = 0;
    flag_killfast = false;
#endif
}

void USBDisk::init()
{
    sdactive = false;
    
#if defined (__SAM3X8E__)
#if !defined(EEPROM_AVAILABLE) || !defined(EEPROM_SPI_ALLIGATOR) || EEPROM_AVAILABLE != EEPROM_SPI_ALLIGATOR
    HAL::spiBegin();
#endif
#else
    HAL::spiInit(SPI_QUARTER_SPEED);  // Set SPI to 2MHz. It's 1/4 of full SPI speed.
    HAL::spiBegin();
#endif
    
    if (spiusbdisk_mount == NULL)
        return;
    
    if (spiusbdisk_mount() == false)
    {
        Com::printFLN(Com::tSDInitFail);
        return;
    }

    sdactive = true;
    Printer::setMenuMode(MENU_MODE_SD_MOUNTED,true);

#if defined (__SAM3X8E__)
    if (HAL::eprGetByte(EPR_SD_PRINTING_START) && (HAL::eprGetInt32(EPR_SD_PRINTING_SDPOS) != 0))
    {
        // Not finished last printing. AND IN LAST PRINTING THE POWER IS OFF BY ACCIDENT. Show the resume dialog
        BEEP_LONG
        uid.executeAction(UI_ACTION_RESUMEPRINT, false);
    }
    else
#endif
    {
        chdir();
        if(selectFile("init.g", true))
        {
            startPrint();
        }
    }
}

void USBDisk::mount()
{
    sdmode = 0;
    init();
}

void USBDisk::unmount()
{
    sdmode = 0;
    sdactive = false;
    savetosd = false;
    Printer::setMenuMode(MENU_MODE_SD_MOUNTED+MENU_MODE_SD_PAUSED+MENU_MODE_SD_PRINTING,false);

    uid.cwd[0]='/';
    uid.cwd[1]=0;
    uid.folderLevel=0;
}

void USBDisk::ls()
{
    Com::printFLN(Com::tBeginFileList);
    if (spiusbdisk_ls_recursive)
        spiusbdisk_ls_recursive(0, 0, "/");
    Com::printFLN(Com::tEndFileList);
}

bool USBDisk::selectFile(char *filename, bool silent)
{
    if (spiusbdisk_open == NULL)
        return false;
    
    char *oldP = filename;

    if(!sdactive) return false;
    sdmode = 0;

    spiusbdisk_close();
    if (spiusbdisk_open(filename, O_READ))
    {
        if ((oldP = strrchr(filename, '/')) != NULL)
            oldP++;
        else
            oldP = filename;

        if(!silent)
        {
            Com::printF(Com::tFileOpened, oldP);
            Com::printFLN(Com::tSpaceSizeColon,spiusbdisk_fileSize());
        }
        sdpos = 0;
        filesize = spiusbdisk_fileSize();
        Com::printFLN(Com::tFileSelected);
        
        filebufpos = -1;
        filebuflen = 0;
        return true;
    }
    else
    {
        if(!silent)
            Com::printFLN(Com::tFileOpenFailed);
        return false;
    }
}

void USBDisk::writeCommand(GCode *code)
{
    unsigned int sum1=0,sum2=0; // for fletcher-16 checksum
    uint8_t buf[100];
    uint8_t p=2;
    int params = 128 | (code->params & ~1);
    *(int*)buf = params;
    if(code->isV2())   // Read G,M as 16 bit value
    {
        *(int*)&buf[p] = code->params2;
        p+=2;
        if(code->hasString())
            buf[p++] = strlen(code->text);
        if(code->hasM())
        {
            *(int*)&buf[p] = code->M;
            p+=2;
        }
        if(code->hasG())
        {
            *(int*)&buf[p]= code->G;
            p+=2;
        }
    }
    else
    {
        if(code->hasM())
        {
            buf[p++] = (uint8_t)code->M;
        }
        if(code->hasG())
        {
            buf[p++] = (uint8_t)code->G;
        }
    }
    if(code->hasX())
    {
        *(float*)&buf[p] = code->X;
        p+=4;
    }
    if(code->hasY())
    {
        *(float*)&buf[p] = code->Y;
        p+=4;
    }
    if(code->hasZ())
    {
        *(float*)&buf[p] = code->Z;
        p+=4;
    }
    if(code->hasE())
    {
        *(float*)&buf[p] = code->E;
        p+=4;
    }
    if(code->hasF())
    {
        *(float*)&buf[p] = code->F;
        p+=4;
    }
    if(code->hasT())
    {
        buf[p++] = code->T;
    }
    if(code->hasS())
    {
        *(long int*)&buf[p] = code->S;
        p+=4;
    }
    if(code->hasP())
    {
        *(long int*)&buf[p] = code->P;
        p+=4;
    }
    if(code->hasI())
    {
        *(float*)&buf[p] = code->I;
        p+=4;
    }
    if(code->hasJ())
    {
        *(float*)&buf[p] = code->J;
        p+=4;
    }
    if(code->hasString())   // read 16 uint8_t into string
    {
        char *sp = code->text;
        if(code->isV2())
        {
            uint8_t i = strlen(code->text);
            for(; i; i--) buf[p++] = *sp++;
        }
        else
        {
            for(uint8_t i=0; i<16; ++i) buf[p++] = *sp++;
        }
    }
    uint8_t *ptr = buf;
    uint8_t len = p;
    while (len)
    {
        uint8_t tlen = len > 21 ? 21 : len;
        len -= tlen;
        do
        {
            sum1 += *ptr++;
            if(sum1>=255) sum1-=255;
            sum2 += sum1;
            if(sum2>=255) sum2-=255;
        }
        while (--tlen);
    }
    buf[p++] = sum1;
    buf[p++] = sum2;
    if(params == 128)
    {
        Com::printErrorFLN(Com::tAPIDFinished);
    }
    else
    {
        int result = usbdisk.writebuffer(buf,p);
        if (result == -1 || result != p)
        {
            Com::printFLN(Com::tErrorWritingToFile);
        }
    }
}

void USBDisk::startPrint()
{
    if(!sdactive) return;
    sdmode = 1;
    Printer::setMenuMode(MENU_MODE_SD_PRINTING,true);
    Printer::setMenuMode(MENU_MODE_SD_PAUSED,false);
}

void USBDisk::pausePrint(bool intern)
{
    if(!sdactive) return;
    sdmode = 2;
    Printer::setMenuMode(MENU_MODE_SD_PAUSED,true);
#if FEATURE_MEMORY_POSITION
    if(intern) {
        Commands::waitUntilEndOfAllBuffers();
        Printer::MemoryPosition();
#if DRIVE_SYSTEM==3
        Printer::moveToReal(0,0.9*EEPROM::deltaMaxRadius(),IGNORE_COORDINATE,IGNORE_COORDINATE,0.5*Printer::maxFeedrate[Z_AXIS]);
#else
        Printer::moveToReal(Printer::xMin,Printer::yMin+Printer::yLength,IGNORE_COORDINATE,IGNORE_COORDINATE,0.5*Printer::maxFeedrate[X_AXIS]);
#endif
    }
#endif
}

void USBDisk::continuePrint(bool intern)
{
    if(!sdactive) return;
    Printer::setMenuMode(MENU_MODE_SD_PAUSED,false);
#if FEATURE_MEMORY_POSITION
    if(intern) {
        Printer::GoToMemoryPosition(true,true,false,true,0.5*Printer::maxFeedrate[X_AXIS]);
        Printer::GoToMemoryPosition(false,false,true,false,0.8*Printer::maxFeedrate[Z_AXIS]);
    }
#endif
    sdmode = 1;
}

void USBDisk::stopPrint()
{
    if(!sdactive) return;
    sdmode = 0;
    Printer::setMenuMode(MENU_MODE_SD_PRINTING,false);
    Printer::setMenuMode(MENU_MODE_SD_PAUSED,false);
    Com::printFLN(PSTR("SD print stopped by user."));
}

void USBDisk::setIndex(uint32_t  newpos)
{
    if (spiusbdisk_seekSet == NULL)
        return;
    
    if(!sdactive) return;
    sdpos = newpos;
    spiusbdisk_seekSet(sdpos);
    filebufpos = -1;
}

void USBDisk::printStatus()
{
    if(sdactive)
    {
        Com::printF(Com::tSDPrintingByte,sdpos);
        Com::printFLN(Com::tSlash,filesize);
    }
    else
    {
        Com::printFLN(Com::tNotSDPrinting);
    }
}

void USBDisk::startWrite(char *filename)
{
    if (spiusbdisk_open == NULL)
        return;
    
    if(!sdactive) return;
    spiusbdisk_close();
    sdmode = 0;
    chdir();
    if(!spiusbdisk_open(filename, O_WRITE))
    {
        Com::printFLN(Com::tOpenFailedFile,filename);
    }
    else
    {
#if defined (__SAM3X8E__)
        UI_STATUS_F(Com::translatedF(UI_TEXT_UPLOADING_ID));
#else
        UI_STATUS(UI_TEXT_UPLOADING);
#endif
        savetosd = true;
        Com::printFLN(Com::tWritingToFile,filename);
    }
}

void USBDisk::finishWrite()
{
    if (spiusbdisk_close == NULL)
        return;
    
    if(!savetosd) return; // already closed or never opened
    //file.sync();
    spiusbdisk_close();
    savetosd = false;
    Com::printFLN(Com::tDoneSavingFile);
    UI_CLEAR_STATUS;
}

bool USBDisk::deleteFile(char *filename)
{
    if (spiusbdisk_remove == NULL)
        return false;
    
    if(!sdactive) return false;
    sdmode = 0;
    spiusbdisk_close();

    if(spiusbdisk_remove(filename))
    {
        Com::printFLN(Com::tFileDeleted);
        return true;
    }
    else
    {
        Com::printFLN(Com::tDeletionFailed);
        return false;
    }
}

void USBDisk::makeDirectory(char *filename)
{
    if (spiusbdisk_mkdir == NULL)
        return;
    
    if(!sdactive) return;
    sdmode = 0;
    spiusbdisk_close();
    
    if(spiusbdisk_mkdir(filename))
    {
        Com::printFLN(Com::tDirectoryCreated);
    }
    else
    {
        Com::printFLN(Com::tCreationFailed);
    }
}

bool USBDisk::chdir()
{
    if (spiusbdisk_chdir == NULL)
        return false;
    
    return spiusbdisk_chdir();
}

bool USBDisk::chdir(const char *path)
{
    // Change to path dir.
    //Com::print("before chdir: ");
    //Com::print(path);
    //Com::println();
    
    if (!chdir())
        return false;
    
    if (spiusbdisk_opn_parent_folders == NULL)
        return false;
    
    bool result = spiusbdisk_opn_parent_folders((char **)&path, false);
    
    //Com::print("after chdir: ");
    //Com::print(path);
    //Com::println();
    
    return result;
}

void USBDisk::listFilesReset()
{
    if (spiusbdisk_ls_reset)
        spiusbdisk_ls_reset();
}

dir_t *USBDisk::listFilesNext()
{
    if (spiusbdisk_ls_next == NULL)
        return false;
    return spiusbdisk_ls_next();
}

#define	USB_INT_SUCCESS		0x14			/* USB事务或者传输操作成功 */

bool USBDisk::getLongFilename(uint8_t *pathname, uint8_t *longname)
{
    if (spiusbdisk_getLongName == NULL)
        return false;
    
    uint8_t s = spiusbdisk_getLongName(pathname, longname);
    if (s != USB_INT_SUCCESS)
    {
        longname[0] = 0;
        //Com::print("getLongFilename failed: ");
        //Com::printHexNum(s);
        //Com::println();
        return false;
    }
    return true;
}

int16_t USBDisk::readbyte()
{
    if (spiusbdisk_fileRead == NULL)
        return -1;
    
    //Com::print("enter readbyte\r\n");
    
    if (filebufpos < 0 || filebufpos >= filebuflen)
    {
        uint16_t len;
        uint8_t s = spiusbdisk_fileRead(filebuf, sizeof(filebuf), &len);
        //Com::print("returned.");
        if (s != USB_INT_SUCCESS) return -1;
        filebuflen = len;
        filebufpos = 0;
    }

    //char b[2];
    //b[0] = filebuf[filebufpos];
    //b[1] = 0;
    
    //Com::print("readbyte: ");
    //Com::print(b);
    //Com::printHexNum(b[0]);
    //Com::println();
    
    return filebuf[filebufpos++];
}

int USBDisk::writebuffer(const void* buf, size_t nbyte)
{
    if (spiusbdisk_fileWrite == NULL)
        return -1;
    
    // Maximium 100 bytes buffer
    uint16_t len;
    uint8_t s = spiusbdisk_fileWrite((uint8_t *)buf, nbyte, &len);
    if (s != USB_INT_SUCCESS) return -1;
    return len;
}

#if defined (__SAM3X8E__)
void USBDisk::killfast()
{
    // Power is off, store sdpos to EEPROM fast!
    Printer::kill(false);  // Turn off everything.
    HAL::eprSetInt32(EPR_SD_PRINTING_SDPOS, usbdisk.sdpos);

    Com::printFLN("usbdisk.sdpos = ", usbdisk.sdpos);

    // Show something in screen? Or just reset the CPU?
}
#endif
#endif
